
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string>

#include "Log_Client.h"

#include "Log_Server.h"

#ifdef USE_TCP_FLAG
    #include "TCPInterface.h"
#else
    #include "UDPInterface.h"
#endif

std::string CLogServer::m_strQueryRspData;
int CLogServer::m_nCallBackIndex = 0;

// main() for the Log Server
int main()
{

    CLogServer logSrv;
    logSrv.AddLogInLogSrv(INFO_LEVEL, "Log_Server", "Log Server Start Running");

    // Loop
    logSrv.WhileMain();

    return 0;
}

void* CLogServer::AcceptThreadHandler(void * arg)
{
    CLogServer *pObj = (CLogServer *)arg;

	//定义一个结构体，存储服务器的路径和协议
    struct sockaddr_in clientAddr;
    memset(&clientAddr, 0, sizeof(clientAddr));

    socklen_t clientAddrLen = sizeof(struct sockaddr);

    while(1)
    {
        // Accept the Connection
        int cliNewSocket = accept(pObj->m_sockFd, (struct sockaddr *)&clientAddr, &clientAddrLen);
        if (cliNewSocket == -1)
        {
            pObj->AddLogInLogSrv(ERROR_LEVEL, "Log_Server", "Error: Failed to accept connection");
            return NULL;
        }

        printf("New Connection, ip %s, port %d \n", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port));

        // Add to the Client List
        int nIndex;
        for (nIndex = 0; nIndex < MAX_TCP_CLIENTS; nIndex++)
        {
            if (pObj->m_clientSocket[nIndex] == -1)
            {
                pthread_mutex_lock(&pObj->m_TcpCliArrayMutex);
                pObj->m_clientSocket[nIndex] = cliNewSocket;
                pthread_mutex_unlock(&pObj->m_TcpCliArrayMutex);
                break;
            }
        }

        if (nIndex == MAX_TCP_CLIENTS)
        {
            pObj->AddLogInLogSrv(ERROR_LEVEL, "Log_Server", "Error: Too many clients");
            close(cliNewSocket);
        }
    }

}

void* CLogServer::AddLogThreadHandler(void * arg)
{
    CLogServer *pObj = (CLogServer *)arg;

    while(1)
    {

        std::vector<LOG_COMM_STU> addLogVt;
        std::string strResponseText;
        if (pObj->GetAllFromAddLogVt(addLogVt))
        {
            int nVtSize = addLogVt.size();
            for (int index = 0; index < nVtSize; index++)
            {
                
                pObj->AddLogExecuteFunc(addLogVt.at(index), strResponseText);
            }
        }

        usleep(50 * 1000);
    }

    printf("AddLogThreadHandler exit\n");
}


CLogServer::CLogServer():m_sqlFd(NULL), m_sockFd(-1)
{
    InitSocket();
    InitSqlLog();

    m_AddLogMutex = PTHREAD_MUTEX_INITIALIZER;
    m_TcpCliArrayMutex = PTHREAD_MUTEX_INITIALIZER;
    
    pthread_t addLogTID;
    pthread_create(&addLogTID, NULL, CLogServer::AddLogThreadHandler, (void *)this);


}


CLogServer::~CLogServer()
{
    int nIndex;
    for (nIndex = 0; nIndex < MAX_TCP_CLIENTS; nIndex++)
    {
        if (m_clientSocket[nIndex] != -1)
        {
            close(m_clientSocket[nIndex]);
        }
    }

    close(m_sockFd);
}


bool CLogServer::GetAllFromAddLogVt(std::vector<LOG_COMM_STU> &addLogVt)
{
    bool bRet = false;

    pthread_mutex_lock(&m_AddLogMutex);

    if (m_AddLogVt.size() >0)
    {
        // printf("[Srv] --- before swap m_AddLogVt.size() = %ld\n", m_AddLogVt.size());
        addLogVt.clear();
        addLogVt.swap(m_AddLogVt);
        // printf("[Srv] --- after swap m_AddLogVt.size() = %ld\n", m_AddLogVt.size());

        bRet = true;
    }
    
    pthread_mutex_unlock(&m_AddLogMutex);


    return bRet;
}


bool CLogServer::InitSocket()
{

#ifdef USE_TCP_FLAG

    int nIndex;
    for (nIndex = 0; nIndex < MAX_TCP_CLIENTS; nIndex++)
    {
        m_clientSocket[nIndex] = -1;
    }


    m_sockFd = SocketInitTCPServer("127.0.0.1", 8888);
    printf("[Successfully] Using TCP for Communication\n");

#else

    //创建套接字---------数据报
	m_sockFd = socket(AF_INET,SOCK_DGRAM,0);
	if(m_sockFd < 0)
	{
		printf("create socket error\n");
		return false;
	}
	

    // 绑定ip地址和端口
    struct sockaddr_in serverAddr;
    memset(&serverAddr, 0, sizeof(struct sockaddr_in));

    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(8888);
    serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    // serverAddr.sin_addr.s_addr = INADDR_ANY;


    if (bind(m_sockFd, (struct sockaddr *)&serverAddr, sizeof(serverAddr))== -1) 
    {
        printf("Could not bind to socket\n");
        return false;
    }

    printf("[Successfully] Using UDP for Communication\n");

    return true;

#endif
}



bool CLogServer::InitSqlLog()
{
    char dbName[100] = "mySqlite.db";

    // open
    int ret = sqlite3_open(dbName, &m_sqlFd);
    if (ret != SQLITE_OK)
    {
        printf("sqlite3 open fail\n");
    }


    // create
    const char *cstrCreateCmd = (char*)"CREATE TABLE IF NOT EXISTS LogTable(date char[15], time char[15], type int, object char[15], detail char[200]);";
    ret = sqlite3_exec(m_sqlFd, cstrCreateCmd, NULL, NULL, NULL);

    if (ret != SQLITE_OK)
    {
        printf("sql create Table error cmd=%s\n", cstrCreateCmd);
        return false;
    }
}



// wait the request all the time, and handle ,fill data in response message
int CLogServer::WhileMain()
{
	//定义一个结构体，存储服务器的路径和协议
    struct sockaddr_in clientAddr;
    memset(&clientAddr, 0, sizeof(clientAddr));

    socklen_t clientAddrLen = sizeof(struct sockaddr);
    char buffer[640] = {0};
    //std::string strRecvData;

#ifdef USE_TCP_FLAG
    // Create a New Thread to accept the new connection
    pthread_t acceptTID;
    pthread_create(&acceptTID, NULL, CLogServer::AcceptThreadHandler, (void *)this);
#endif


    int max_sd = 0;
    int sockedItemFd = 0;
    int valRead = 0;
    struct sockaddr_in clientAddressSt;
    int addrlen = sizeof(clientAddressSt);
    
    struct timeval timeout;


    while (true) 
    {
#ifdef USE_TCP_FLAG
        FD_ZERO(&m_readfds);
        pthread_mutex_lock(&m_TcpCliArrayMutex);
        for (int i = 0; i < MAX_TCP_CLIENTS; i++) 
        {
            sockedItemFd = m_clientSocket[i];
            if (sockedItemFd > 0) 
            {
                FD_SET(sockedItemFd, &m_readfds);
                if (sockedItemFd > max_sd) 
                {
                    max_sd = sockedItemFd;
                }
            }
        }
        pthread_mutex_unlock(&m_TcpCliArrayMutex);

        // 设置超时时间为5秒
        timeout.tv_sec = 2;
        timeout.tv_usec = 0;
        
        int activity = select(max_sd + 1, &m_readfds, NULL, NULL, &timeout);
        if ((activity < 0) && (errno != EINTR)) 
        {
            printf("select error");
            continue;
        } 
        else if (activity == 0) 
        {
            // printf("select timeout occurred, max_sd = %d\n", max_sd);
            continue;
        }

        for (int i = 0; i < max_sd + 1; i++) 
        {
            pthread_mutex_lock(&m_TcpCliArrayMutex);
            sockedItemFd = m_clientSocket[i];
            pthread_mutex_unlock(&m_TcpCliArrayMutex);
            if (FD_ISSET(sockedItemFd, &m_readfds)) 
            {
                char buffer[1024 * 10];
                int nBytesReceived = recv(sockedItemFd, buffer, sizeof(buffer), 0);
                if (nBytesReceived == 0) 
                {
                    getpeername(sockedItemFd, (struct sockaddr*)&clientAddressSt, (socklen_t*)&addrlen);
                    printf("Host disconnected, ip %s, port %d \n", inet_ntoa(clientAddressSt.sin_addr), ntohs(clientAddressSt.sin_port));
                    AddLogInLogSrv(INFO_LEVEL, "Log_Server", "Host disconnected, ip %s, port %d", inet_ntoa(clientAddressSt.sin_addr), ntohs(clientAddressSt.sin_port));
                    close(sockedItemFd);
                    m_clientSocket[i] = -1;
                    continue;
                }
                else if (nBytesReceived < 0)
                {
                    printf("Log Server Error receiving data----------\n");
                    AddLogInLogSrv(ERROR_LEVEL, "Log_Server", "Error: Failed to receive Data From New Client Socket");
                    close(sockedItemFd);
                    m_clientSocket[i] = -1;
                    continue;
                }
                LOG_COMM_STU recvStu = *(LOG_COMM_STU*)buffer;

                // [Handle] fill the response message
                std::string strRspData;
                ProcessCmdFunc(recvStu, strRspData);


                if (SendDataByTCP(sockedItemFd, strRspData.c_str(), strRspData.length(), 0) == -1)
                {
                    printf("Log Server Error sending data\n");
                    AddLogInLogSrv(ERROR_LEVEL, "Log_Server", "Error: Failed to send response to New Client Socket.\n");
                }
            }
        }

#else
        // [Recv]
		int ret = recvfrom(m_sockFd, buffer, sizeof(buffer), 0, (struct sockaddr *)&clientAddr, &clientAddrLen);
        //int ret = RecvDataByUDP(m_sockFd, strRecvData,  0, (struct sockaddr *)&clientAddr, &clientAddrLen);
        if (ret < 0)
        {
            printf("Log Server Error receiving data\n");
            continue;
        }

		LOG_COMM_STU recvStu = *(LOG_COMM_STU*)buffer;

        // [Handle] fill the response message
        std::string strRspData;
        ProcessCmdFunc(recvStu, strRspData);

        // Get Result to Client      
        ret = SendDataByUDP(m_sockFd, strRspData.c_str(), strRspData.size(), 0, (struct sockaddr*)&clientAddr, sizeof(clientAddr));
        // printf("strRspData.size() = %ld, ret = %d\n", strRspData.size(), ret);
        if (ret < 0)
        {
            printf("WhileMain() Log Server Error receiving data\n");
            return -1;
        }
#endif

    }

    return 0;
}

// call the Handler function by operation type
bool CLogServer::ProcessCmdFunc(const LOG_COMM_STU &recvStu, std::string &strRspData)
{
    // printf("CLogServer::ProcessCmdFunc()  recvStu.optType = %d\n", recvStu.optType);
    switch(recvStu.optType)
    {
        case OPT_ADD_LOG:
            {
                //Push to deque
                pthread_mutex_lock(&m_AddLogMutex);

                LOG_COMM_STU recvItem = recvStu;
                m_AddLogVt.push_back(recvItem);
                pthread_mutex_unlock(&m_AddLogMutex);
                
                break;
            }
            
        case OPT_DEL_LOG:
            DelLogExecuteFunc(recvStu, strRspData);
            break;
        case OPT_CLEAR_LOG:
            ClearLogExecuteFunc(recvStu, strRspData);
            break;
        case OPT_QUERY_LOG:
            QueryLogExecuteFunc(recvStu, strRspData);
            break;
        default:
            printf("wrong Type = %d\n", recvStu.optType);

    }

    return 0;
}


int CLogServer::AddLogInLogSrv(LOG_LEVEL nLogLevel, const std::string &strSource, const char* logInfo, ...)
{
    std::string strDate;
    std::string strTime;
    if (GetCurrentDateTime(strDate, strTime) == false)
    {
        return false;
    }


    char cstrLogBuff[1024];
    va_list valist;
    va_start(valist, logInfo);
    vsnprintf(cstrLogBuff, sizeof(cstrLogBuff), logInfo,valist);
    va_end(valist);

    std::string strDetailInfo = cstrLogBuff;

    // printf("[Log In Server] = %s\n", strDetailInfo.c_str());


    char cstrCmd[150]={0};
    snprintf(cstrCmd, sizeof(cstrCmd), "insert into LogTable values(\"%s\", \"%s\", \"%d\", \"%s\", \"%s\");", 
        strDate.c_str(), strTime.c_str(), nLogLevel, strSource.c_str(), strDetailInfo.c_str());

    int ret = sqlite3_exec(m_sqlFd, cstrCmd, NULL, NULL, NULL);
    if (ret != SQLITE_OK)
    {
        printf("sql excuate fail, cmd = %s", cstrCmd);
        return false;
    }

}


bool CLogServer::AddLogExecuteFunc(const LOG_COMM_STU &recvStu, std::string &strRspData)
{
    // maybe mutex
    if (m_sqlFd == NULL)
    {
        return false;
    }

    // Parse
    LOG_LEVEL logType;
    std::string strObj;
    std::string strDetailInfo;
    AddLogPraseMessage(recvStu, logType, strObj, strDetailInfo);
    printf("----------------------strObj = %s, strDetailInfo = %s\n",strObj.c_str(), strDetailInfo.c_str());

    std::string strDate;
    std::string strTime;
    if (GetCurrentDateTime(strDate, strTime) == false)
    {
        return false;
    }


    char cstrCmd[150]={0};
    snprintf(cstrCmd, sizeof(cstrCmd), "insert into LogTable values(\"%s\", \"%s\", \"%d\", \"%s\", \"%s\");", 
        strDate.c_str(), strTime.c_str(), logType, strObj.c_str(), strDetailInfo.c_str());



    int ret = sqlite3_exec(m_sqlFd, cstrCmd, NULL, NULL, NULL);
    if (ret != SQLITE_OK)
    {
        AddLogInLogSrv(ERROR_LEVEL, "Log Server", "sql excuate fail, cmd = %s", cstrCmd);
        return false;
    }

    strRspData = "Add Log Successfully";
    return true;
}

// sqlite3 operation
bool CLogServer::DelLogExecuteFunc(const LOG_COMM_STU &recvStu, std::string &strRspData)
{
    if (m_sqlFd == NULL)
    {
        return false;
    }

    LOG_LEVEL logType;
    int nCount;
    DelLogPraseMessage(recvStu, logType, nCount);

    
    char cstrCmd[180] = {0};
    snprintf(cstrCmd, sizeof(cstrCmd), "DELETE FROM LogTable WHERE rowid IN (SELECT rowid FROM LogTable ORDER BY date ASC, time ASC LIMIT %d);", nCount);

    int ret = sqlite3_exec(m_sqlFd, cstrCmd, NULL, NULL, NULL);
    if (ret != SQLITE_OK)
    {
        AddLogInLogSrv(ERROR_LEVEL, "Log Server", "sql excuate fail, cmd = %s", cstrCmd);
        return false;
    }

    AddLogInLogSrv(INFO_LEVEL, "Log Server", "sql excuate successfully, Delete Log logType = %d, nCount = %d", logType, nCount);

    strRspData = "Delete Log Successfully";
    return true;
}


bool CLogServer::ClearLogExecuteFunc(const LOG_COMM_STU &recvStu, std::string &strRspData)
{
    char cstrCmd[150]="delete from LogTable;";
    
    if (m_sqlFd == NULL)
    {
        return false;
    }

    LOG_LEVEL logType;
    ClearLogPraseMessage(recvStu, logType);

    int ret = sqlite3_exec(m_sqlFd, cstrCmd, NULL, NULL, NULL);
    if (ret != SQLITE_OK)
    {
        AddLogInLogSrv(ERROR_LEVEL, "Log Server", "sql excuate fail, cmd = %s", cstrCmd);
        return false;
    }
    AddLogInLogSrv(INFO_LEVEL, "Log Server", "sql excuate successfully, Clear Log logType = %d ", logType);


    strRspData = "Clear Log Successfully";
    return true;
}

// 数据库中  查看表头，并且列对齐，敲命令 ".header on" 和 ".mode column"
bool CLogServer::QueryLogExecuteFunc(const LOG_COMM_STU &recvStu, std::string &strRspData)
{
    if (m_sqlFd == NULL)
    {
        return false;
    }

#if 1
    char sql[256] = "SELECT COUNT(*) FROM LogTable;";

    int rc = sqlite3_exec(m_sqlFd, sql, QueryCountCallback, NULL, NULL);

    if (rc != SQLITE_OK) 
    {
        printf("SQL Execuate Error, cmd = %s\n", sql);
    } 

#endif

    LOG_LEVEL logType;
    int nCount;
    std::string strKeyWord;
    std::string strStartDateTime;
    std::string strEndDateTime;

    if (QueryLogPraseMessage(recvStu, logType, nCount, strKeyWord, strStartDateTime, strEndDateTime) == false)
    {
        return false;
    }
    // printf("nCount = %d, strKeyWord = %s\n", nCount, strKeyWord.c_str());
    m_strQueryRspData.clear();


    std::string strCmd;
    // '%' (indicating any number of characters)
    if (nCount <= 0)
    {
        strCmd = "SELECT * FROM LogTable WHERE detail LIKE '%" + strKeyWord + "%' ORDER BY date DESC, time DESC ;";
        // snprintf(cstrCmd, sizeof(cstrCmd), "SELECT * FROM LogTable WHERE detail LIKE '\%%s%';", strKeyWord.c_str());
    }
    else
    {
        strCmd = "SELECT * FROM LogTable WHERE detail LIKE '%" + strKeyWord + "%'  ORDER BY date DESC, time DESC LIMIT " + std::to_string(nCount) + ";";
        // snprintf(cstrCmd, sizeof(cstrCmd), "SELECT * FROM LogTable WHERE detail LIKE '\%%s%' ORDER BY date DESC, time DESC LIMIT  %d;", strKeyWord.c_str(), nCount);
    }

    int ret = sqlite3_exec(m_sqlFd, strCmd.c_str(), QueryLogCallback, NULL, NULL);
    if (ret != SQLITE_OK)
    {
        AddLogInLogSrv(ERROR_LEVEL, "Log Server", "sql excuate fail, cmd = %s", strCmd.c_str());
        return false;
    }

    AddLogInLogSrv(INFO_LEVEL, "Log Server", "Query Log Successfully, nCount = %d, strKeyWord = %s", nCount, strKeyWord.c_str());

    strRspData = m_strQueryRspData;
    m_nCallBackIndex = 0;
    return true;
}



// ================== [Prase the message struct from Client] ==================
bool CLogServer::AddLogPraseMessage(const LOG_COMM_STU &commStu, LOG_LEVEL &logType, std::string &strObj, std::string &strDetailInfo)
{
    if (commStu.optType != OPT_ADD_LOG)
    {
        printf("[%s] commStu.optType != OPT_ADD_LOG\n", __func__);
        return false;
    }
    
    LOG_INFO_STU addLogStu = *(LOG_INFO_STU*)commStu.allDataInfo;

    logType = addLogStu.type;
    strObj  = addLogStu.objectArr;
    strDetailInfo = addLogStu.detailArr;

    return true;
}

bool CLogServer::DelLogPraseMessage(const LOG_COMM_STU &commStu, LOG_LEVEL &logType, int &nCount)
{
    if (commStu.optType != OPT_DEL_LOG)
    {
        return false;
    }
    
    LOG_DEL_STU delLogStu;
    memcpy((char *)&delLogStu, commStu.allDataInfo, sizeof(delLogStu));

    logType = (LOG_LEVEL)delLogStu.type;
    nCount = delLogStu.nCount;

    return true;
}

bool CLogServer::ClearLogPraseMessage(const LOG_COMM_STU &commStu, LOG_LEVEL &logType)
{
    if (commStu.optType != OPT_CLEAR_LOG)
    {
        return false;
    }
    
    LOG_DEL_STU delLogStu;
    memcpy((char *)&delLogStu, commStu.allDataInfo, sizeof(delLogStu));

    logType = (LOG_LEVEL)delLogStu.type;


    return true;
}

bool CLogServer::QueryLogPraseMessage(const LOG_COMM_STU &commStu, LOG_LEVEL &logType, int &nCount, std::string &strKeyWord, std::string &strStartDateTime, std::string &strEndDateTime)
{
    if (commStu.optType != OPT_QUERY_LOG)
    {
        return false;
    }
    
    LOG_QUERY_STU queryLogStu;
    memcpy((char *)&queryLogStu, commStu.allDataInfo, sizeof(queryLogStu));

    logType = (LOG_LEVEL)queryLogStu.type;
    nCount = queryLogStu.nCount;
    strKeyWord = queryLogStu.keyword;
    strStartDateTime = queryLogStu.startDateTimeArr;
    strEndDateTime = queryLogStu.endDateTimeArr;

    return true;
}


bool CLogServer::GetCurrentDateTime(std::string &strCurrentDate, std::string &strCurrentTime)
{
    time_t now = time(NULL);  // 获取当前时间

    // Local Time
    struct tm locTm;
    memset(&locTm, 0, sizeof(locTm));
    localtime_r(&now, &locTm);

    
    char tmpBuff[20];
    strftime(tmpBuff, sizeof(tmpBuff), "%Y-%m-%d", &locTm);  // 将时间戳转换为字符串
    strCurrentDate = tmpBuff;

    memset(&tmpBuff, 0, sizeof(tmpBuff));
    strftime(tmpBuff, sizeof(tmpBuff), "%H:%M:%S", &locTm);  // 将时间戳转换为字符串

    int millisecond = clock() % 1000;
    char cstrMillisecond[4];
    snprintf(cstrMillisecond, sizeof(cstrMillisecond), "%03d", millisecond);

    strCurrentTime = std::string(tmpBuff) + "." + std::string(cstrMillisecond);


    return true;

}

// Call back function for the query operation, and also can be customize by using the function pointer.
int CLogServer::QueryLogCallback(void* data, int argc, char** argv, char** azColName)
{
    std::string strItem;
    int i = 0;
    if(m_nCallBackIndex == 0)
    {
        // Title
        for (i = 0; i < argc - 1; i++)
        {
            strItem = strItem + azColName[i] + "   |   ";
        }
        strItem = strItem + + azColName[i] + "\n";
    }

    m_nCallBackIndex++;
    

    for (i = 0; i < argc - 1; i++) 
    {
        // printf("[%s] = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
        strItem = strItem + argv[i] + " | ";
    }

    // printf("strItem = %s\n", strItem.c_str());

    m_strQueryRspData = m_strQueryRspData + strItem + argv[i] + "\n";

    return 0;
}


int CLogServer::QueryCountCallback(void* data, int argc, char** argv, char** azColName)
{
   int i;
   for (i = 0; i < argc; i++) 
   {
      printf("%s === %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
   }
   printf("\n");
   return 0;
}